package com.version1.diabeco.data.comida;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.version1.diabeco.data.AbstractDao;
import com.version1.diabeco.data.DiabecoContract;
import com.version1.diabeco.model.ajustes.medicamento.Medicamento;
import com.version1.diabeco.model.alimento.Alimento;
import com.version1.diabeco.model.comida.Comida;
import com.version1.diabeco.model.comida.DetalleComida;
import com.version1.diabeco.model.glucosa.Glucosa;

public final class ComidaDaoImpl extends AbstractDao implements ComidaDao {

	private Context context;

	private static ComidaDaoImpl instance;

	private ComidaDaoImpl(Context context) {
		super(context);
	}

	/**
	 * Retrieves the singleton instance. Receives a context which is used for database initialization but also stored
	 * for subsequent sql operation calls.
	 * 
	 * This method is thread-safe.
	 * 
	 * @return AlimentoDao with the instance.
	 */
	public static synchronized ComidaDaoImpl getInstance(Context context) {
		if (instance == null) {
			instance = new ComidaDaoImpl(context);
		}

		instance.setContext(context);

		return instance;
	}

	public Context getContext() {
		return context;
	}

	public void setContext(Context context) {
		this.context = context;
	}

	@Override
	public Long count() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Comida selectById(Long id) {

		Comida result = null;

		SQLiteDatabase db = ComidaDaoImpl.getInstance(this.getContext()).getReadableDatabase();

		List<DetalleComida> detalleComida = this.loadDetalleComida(db, id);

		String[] projection = { DiabecoContract.RegistroComida._ID, DiabecoContract.RegistroComida.COLUMNA_FECHA,
				DiabecoContract.RegistroComida.COLUMNA_CUANDO, DiabecoContract.RegistroComida.COLUMNA_ALIAS };
		String selection = DiabecoContract.RegistroComida._ID + " = ?";
		String[] selectionArgs = { id.toString() };
		String sortOrder = DiabecoContract.RegistroComida.COLUMNA_FECHA + " DESC";

		Cursor cursor = db.query(DiabecoContract.RegistroComida.TABLE_NAME, // The table to query
				projection, // The columns to return
				selection, // The columns for the WHERE clause
				selectionArgs, // The values for the WHERE clause
				null, // don't group the rows
				null, // don't filter by row groups
				sortOrder // The sort order
				);

		cursor.moveToFirst();
		result = this.comidaToDomainObjectList(cursor);
		result.setDetalleComida(detalleComida);

		return result;

	}

	private List<DetalleComida> loadDetalleComida(SQLiteDatabase db, Long id) {
		List<DetalleComida> result = new ArrayList<DetalleComida>();

		String[] projection = { DiabecoContract.RegistroDetalleComida._ID,
				DiabecoContract.RegistroDetalleComida.COLUMNA_ALIAS,
				DiabecoContract.RegistroDetalleComida.COLUMNA_HDECARBONO_AJUSTADO,
				DiabecoContract.RegistroDetalleComida.COLUMNA_ID_COMIDA,
				DiabecoContract.RegistroDetalleComida.COLUMNA_ID_ALIMENTO};
		String selection = DiabecoContract.RegistroDetalleComida.COLUMNA_ID_COMIDA + " = ?";
		String[] selectionArgs = { id.toString() };

		Cursor cursor = db.query(DiabecoContract.RegistroDetalleComida.TABLE_NAME, // The table to query
				projection, // The columns to return
				selection, // The columns for the WHERE clause
				selectionArgs, // The values for the WHERE clause
				null, // don't group the rows
				null, // don't filter by row groups
				null // The sort order
				);

		while (cursor.moveToNext()) {
			DetalleComida detalleComida = this.detalleComidaToDomainObjectList(cursor);
			result.add(detalleComida);
		}
		return result;
	}

	@Override
	public List<Comida> selectBetweenDates(Date from, Date to) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Comida> selectAll() {
		List<Comida> result = new ArrayList<Comida>();

		SQLiteDatabase db = ComidaDaoImpl.getInstance(this.getContext()).getReadableDatabase();

		// Define a projection that specifies which columns from the database
		// you will actually use after this query.
		String[] projection = { DiabecoContract.RegistroComida._ID, DiabecoContract.RegistroComida.COLUMNA_FECHA,
				DiabecoContract.RegistroComida.COLUMNA_CUANDO, DiabecoContract.RegistroComida.COLUMNA_ALIAS };

		String selection = null;
		String[] selectionArgs = null;

		// How you want the results sorted in the resulting Cursor
		String sortOrder = DiabecoContract.RegistroComida.COLUMNA_FECHA + " DESC";

		Cursor cursor = db.query(DiabecoContract.RegistroComida.TABLE_NAME, // The table to query
				projection, // The columns to return
				selection, // The columns for the WHERE clause
				selectionArgs, // The values for the WHERE clause
				null, // don't group the rows
				null, // don't filter by row groups
				sortOrder // The sort order
				);

		while (cursor.moveToNext()) {
			Comida comida = this.comidaToDomainObjectList(cursor);
			
			// TODO: evaluar performance por la cantidad de queries.
			List<DetalleComida> detalleComida = this.loadDetalleComida(db, comida.getId());
			
			comida.setDetalleComida(detalleComida);
			result.add(comida);
		}

		return result;
	}

	@Override
	public List<Comida> select5Dias() {

		// FIXME deberia filtrar directo en la base.
		List<Comida> lista5dias = new ArrayList<Comida>();
		List<Comida> listaCompleta = this.selectAll();
		 
		Calendar c = Calendar.getInstance();
		Date now = new Date();
		c.setTime(now);
		c.add(Calendar.DATE, -5);
		c.clear(Calendar.HOUR_OF_DAY);
		c.clear(Calendar.MINUTE);
		Long hace5diasDate = c.getTime().getTime();

		for (Comida comida : listaCompleta) {
			if (comida.getFecha() > hace5diasDate) {
				lista5dias.add(comida);
			}
		}
		
		return lista5dias;
	}
	
	public void save(Comida comida) {

		// Gets the data repository in write mode
		SQLiteDatabase db = ComidaDaoImpl.getInstance(this.getContext()).getWritableDatabase();
		db.beginTransaction();

		try {
			
			long rowsAffected = 0;
			long comidaId;
	
			ContentValues values = new ContentValues();
			values.put(DiabecoContract.RegistroComida.COLUMNA_FECHA, comida.getFecha());
			values.put(DiabecoContract.RegistroComida.COLUMNA_CUANDO, comida.getCuando());

			if (comida.getId() != null) {
				comidaId = comida.getId();

				// borro la comida al carajo asi se borran los detalles
				db.execSQL("PRAGMA foreign_keys = ON;");
				this.doDeleteById(db, comidaId);

//				rowsAffected = db.update(DiabecoContract.RegistroComida.TABLE_NAME, values,
//						DiabecoContract.RegistroComida._ID + " = ?", new String[] { comida.getId().toString() });
//				System.out.println("cantidad comida actualizados: " + rowsAffected);
				
			}
			// Insert the new row, returning the primary key value of the new row
			comidaId = db.insert(DiabecoContract.RegistroComida.TABLE_NAME, null, values);
			System.out.println("comida id: " + comidaId);
	
			for (DetalleComida detalleComida : comida.getDetalleComida()) {
				ContentValues detalleComidaValues = new ContentValues();
				detalleComidaValues.put(DiabecoContract.RegistroDetalleComida.COLUMNA_HDECARBONO_AJUSTADO, detalleComida
						.getCarbohidratosAjustados());
				detalleComidaValues.put(DiabecoContract.RegistroDetalleComida.COLUMNA_ID_COMIDA, comidaId);
				detalleComidaValues.put(DiabecoContract.RegistroDetalleComida.COLUMNA_ID_ALIMENTO, detalleComida
						.getAlimento().getId());

				// por el cascade, todos los detalles se borran
//				if (detalleComida.getId() != null) {
//					rowsAffected = db.update(DiabecoContract.RegistroDetalleComida.TABLE_NAME, detalleComidaValues,
//							DiabecoContract.RegistroDetalleComida._ID + " = ?", new String[] { detalleComida.getId().toString() });
//					System.out.println("cantidad detalle comida actualizados: " + rowsAffected);
//				} else {
					// Insert the new row, returning the primary key value of the new row
					long detalleComidaId = db.insert(DiabecoContract.RegistroDetalleComida.TABLE_NAME, null, detalleComidaValues);
					System.out.println("detalle comida id: " + detalleComidaId);
//				}
				
			}
	
			db.setTransactionSuccessful();
	   } finally {
	     db.endTransaction();
	   }

	}

	@Override
	public void deleteById(Long comidaId) {

		// Gets the data repository in write mode
		SQLiteDatabase db = ComidaDaoImpl.getInstance(this.getContext()).getWritableDatabase();
		// Activo foreign keys
		db.execSQL("PRAGMA foreign_keys = ON;");
		
		this.doDeleteById(db, comidaId);
	}

	private void doDeleteById(SQLiteDatabase db, Long comidaId) {

		String removeId = comidaId.toString();

		int rowsDeleted = db.delete(DiabecoContract.RegistroComida.TABLE_NAME, DiabecoContract.RegistroComida._ID
				+ "=?", new String[] { removeId });

		if (rowsDeleted != 1) {
			throw new RuntimeException("No se pudo eliminar el registro con id " + comidaId);
		}

	}

	@Override
	public void deleteDetalleComidaById(Long id) {

		// Gets the data repository in write mode
		SQLiteDatabase db = ComidaDaoImpl.getInstance(this.getContext()).getWritableDatabase();

		this.doDeleteDetalleComidaById(db, id);
	}

	private void doDeleteDetalleComidaById(SQLiteDatabase db, Long id) {
		String removeId = id.toString();

		int rowsDeleted = db.delete(DiabecoContract.RegistroDetalleComida.TABLE_NAME,
				DiabecoContract.RegistroDetalleComida._ID + "=?", new String[] { removeId });

		if (rowsDeleted != 1) {
			throw new RuntimeException("No se pudo eliminar el registro con id " + id);
		}
	}
	
	@Override
	public void clear() {
		// TODO Auto-generated method stub

	}

	private DetalleComida detalleComidaToDomainObjectList(Cursor cursor) {
		DetalleComida result = new DetalleComida();

		List<Alimento> alimentos = AlimentoDaoImpl.getInstance(context).selectAll();

		Long id = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.RegistroDetalleComida._ID));
		Long hdc = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.RegistroDetalleComida.COLUMNA_HDECARBONO_AJUSTADO));

		result.setId(id);
		result.setCarbohidratosAjustados(hdc);

		Long idAlimentacion = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.RegistroDetalleComida.COLUMNA_ID_ALIMENTO));
		
		for (Alimento alimento : alimentos) {
			if (alimento.getId().equals(idAlimentacion)) {
				result.setAlimento(alimento);
			}
		}

		System.out.println("Carga detalle comida: " + result);

		return result;
	}

	private Comida comidaToDomainObjectList(Cursor cursor) {
		Comida comida = new Comida();

		Long id = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.RegistroComida._ID));
		Long fecha = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.RegistroComida.COLUMNA_FECHA));
		String cuando = cursor.getString(cursor.getColumnIndexOrThrow(DiabecoContract.RegistroComida.COLUMNA_CUANDO));

		comida.setId(id);
		comida.setFecha(fecha);
		comida.setCuando(cuando);

		System.out.println("Readed food intake measure: " + comida);

		return comida;
	}

}
